/**
 * Copyright (c) 2005-2010 springside.org.cn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * 
 * $Id: SimpleHibernateDao.java 1205 2010-09-09 15:12:17Z calvinxiu $
 */
package com.platform.dao.base;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import javax.persistence.Query;

import org.hibernate.Hibernate;
import org.springframework.orm.jpa.support.JpaDaoSupport;
import org.springframework.util.Assert;

import com.platform.internal.OrderList;
import com.platform.utils.ReflectionUtils;

/**
 * 基于JpaDaoSupport API的DAO泛型基类.
 * 
 * 可在Service层直接使用, 也可以扩展泛型DAO子类使用, 见两个构造函数的注释. 参考Spring2.5自带的Petlinc例子
 * 
 * @param <T>
 *            DAO操作的对象类型
 * @param <PK>
 *            主键类型
 * 
 * @author calvin, WANG Fei
 */
public class SimpleHibernateDAO<T, PK extends Serializable> extends JpaDaoSupport {

	public Class<T> entityClass;

	/**
	 * 用于Dao层子类使用的构造函数. 通过子类的泛型定义取得对象类型Class. eg. public class UserDao extends
	 * SimpleHibernateDao<User, Long>
	 */
	public SimpleHibernateDAO() {
		this.entityClass = ReflectionUtils.getSuperClassGenricType(this.getClass());
	}

	/**
	 * 用于用于省略Dao层, 在Service层直接使用通用SimpleHibernateDao的构造函数. 在构造函数中定义对象类型Class.
	 */
	public SimpleHibernateDAO(final Class<T> entityClass) {
		this.entityClass = entityClass;
	}
	
	/**
	 * 初始化对象. 使用load()方法得到的仅是对象Proxy, 在传到View层前需要进行初始化. 如果传入entity,
	 * 则只初始化entity的直接属性,但不会初始化延迟加载的关联集合和属性. 如需初始化关联属性,需执行:
	 * Hibernate.initialize(user.getRoles())，初始化User的直接属性和关联集合.
	 * Hibernate.initialize
	 * (user.getDescription())，初始化User的直接属性和延迟加载的Description属性.
	 */
	public void initProxyObject(final Object proxy) {
		Hibernate.initialize(proxy);
	}

	/**
	 * Flush当前Session.
	 */
	public void flush() {
		this.getJpaTemplate().getEntityManager().flush();
	}

	/**
	 * 判断对象的属性值在数据库内是否唯一.
	 * 
	 * 在修改对象的情景下,如果属性新修改的值(value)等于属性原来的值(orgValue)则不作比较.
	 */
	public boolean isPropertyUnique(final String propertyName, final Object newValue,
	        final Object oldValue) {
		if (newValue == null || newValue.equals(oldValue)) {
			return true;
		}
		final Object object = this.findUniqueBy(propertyName, newValue);
		
		return (object == null);
	}
	
	/**
	 * 保存新增或修改的对象.
	 */
	public void save(final T entity) {
		Assert.notNull(entity, "entity不能为空");
		
		this.getJpaTemplate().persist(entity);
	}

	/**
	 * 删除对象.
	 * 
	 * @param entity
	 *            对象必须是session中的对象或含id属性的transient对象.
	 */
	public void delete(final T entity) {
		Assert.notNull(entity, "entity不能为空");
		
		this.getJpaTemplate().remove(entity);
	}

	/**
	 * 按id删除对象.
	 */
	public void delete(final PK id) {
		Assert.notNull(id, "id不能为空");
		
		this.delete(this.findById(id));
	}

	/**
	 * 获取全部对象.
	 */
	public List<T> findAll() {
		final String queryString = this.generateQueryString();
		
		return this.find(queryString);
	}
	
	/**
	 * 按id获取对象.
	 */
	public T findById(final PK id) {
		Assert.notNull(id, "id不能为空");
		
		return this.getJpaTemplate().find(this.entityClass, id);
	}
    
	/**
	 * 按属性查找对象列表, 匹配方式为相等.
	 */
	@SuppressWarnings("unchecked")
    public List<T> findBy(final String propertyName, final Object value, final OrderList orderList) {
		Assert.hasText(propertyName, "propertyName不能为空");
		
		String queryString = this.generateQueryString(orderList, propertyName);

		return this.getJpaTemplate().find(queryString, value);
	}
    
    
	
	/**
	 * 按属性查找唯一对象, 匹配方式为相等.
	 */
	@SuppressWarnings("unchecked")
    public T findUniqueBy(final String propertyName, final Object value) {
		Assert.hasText(propertyName, "propertyName不能为空");
		
		final String queryString = this.generateQueryString(propertyName);
		
		return (T) this.createQuery(queryString, value).getSingleResult();
	}

	/**
	 * 按QL查询唯一对象.
	 * 
	 * @param values
	 *            数量可变的参数,按顺序绑定.
	 */
	@SuppressWarnings("unchecked")
    public <X> X findUnique(final String ql, final Object... values) {
		return (X) this.createQuery(ql, values).getSingleResult();
	}
	

	/**
	 * 按QL查询唯一对象.
	 * 
	 * @param values
	 *            命名参数,按名称绑定.
	 */
	@SuppressWarnings("unchecked")
    public <X> X findUnique(final String ql, final Map<String, ?> values) {
		return (X) this.createQuery(ql, values).getSingleResult();
	}
	
	/**
	 * 按QL查询对象列表.
	 * 
	 * @param values
	 *            数量可变的参数,按顺序绑定.
	 */
	@SuppressWarnings("unchecked")
    public <X> List<X> find(final String ql, final Object... values) {
		return this.getJpaTemplate().find(ql, values);
	}

	/**
	 * 按QL查询对象列表.
	 * 
	 * @param values
	 *            命名参数,按名称绑定.
	 */
	@SuppressWarnings("unchecked")
    public <X> List<X> find(final String ql, final Map<String, ?> values) {
		return this.getJpaTemplate().findByNamedParams(ql, values);
	}
	
	/**
	 * 执行QL进行批量修改/删除操作.
	 * 
	 * @param values
	 *            数量可变的参数,按顺序绑定.
	 * @return 更新记录数.
	 */
	public int batchExecute(final String ql, final Object... values) {
		return this.createQuery(ql, values).executeUpdate();
	}

	/**
	 * 执行QL进行批量修改/删除操作.
	 * 
	 * @param values
	 *            命名参数,按名称绑定.
	 * @return 更新记录数.
	 */
	public int batchExecute(final String ql, final Map<String, ?> values) {
		return this.createQuery(ql, values).executeUpdate();
	}
	
	/**
	 * 根据指定的参数名称生成一个QL语句, 匹配方式为相等.
	 * 
	 * @param
	 *             排序规则
	 * @param propertyNames
	 *             参数名称
	 * @return
	 */
	protected String generateQueryString(final OrderList orderList, final String... propertyNames) {
		final StringBuilder queryString = new StringBuilder();
		
		queryString.append("select obj from ")
				   .append(this.entityClass.getSimpleName())
				   .append(" obj where 1 = 1 ");
		
		if(propertyNames != null) {
			for(String name : propertyNames) {
				queryString.append("and ").append(name).append(" = ? ");
			}
		}
		
		if (orderList != null) {
			queryString.append(orderList.toString());
		}
		return queryString.toString();
	}
	
	/**
	 * 根据指定的参数名称生成一个QL语句
	 * 
	 * @param propertyNames
	 *             参数名称
	 * @return
	 */
	protected String generateQueryString(final String... propertyNames) {
		return this.generateQueryString(null, propertyNames);
	}

	/**
	 * 根据查询QL与参数列表创建Query对象. 与find()函数可进行更加灵活的操作.
	 * 
	 * @param values
	 *            数量可变的参数,按顺序绑定.
	 */
	protected Query createQuery(final String queryString, final Object... values) {
		Assert.hasText(queryString, "queryString不能为空");
		
		final Query query = this.getJpaTemplate().getEntityManager().createQuery(queryString);
		
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query;
	}

	/**
	 * 根据查询QL与参数列表创建Query对象. 与find()函数可进行更加灵活的操作.
	 * 
	 * @param values
	 *            命名参数,按名称绑定.
	 */
	protected Query createQuery(final String queryString, final Map<String, ?> values) {
		Assert.hasText(queryString, "queryString不能为空");
		
		final Query query = this.getJpaTemplate().getEntityManager().createQuery(queryString);
		
		if (values != null) {
			for(Map.Entry<String, ?> entry : values.entrySet()) {
				query.setParameter(entry.getKey(), entry.getValue());
			}
		}
		return query;
	}

	public Class<T> getEntityClass() {
    	return this.entityClass;
    }

	public void setEntityClass(Class<T> entityClass) {
    	this.entityClass = entityClass;
    }
}